/*
 * $AIST_Release: 0.9.0 $
 * Copyright 2011 Information Technology Research Institute, National
 * Institute of Advanced Industrial Science and Technology
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package data

import java.lang.reflect.Modifier
import java.lang.reflect.Type
import java.lang.reflect.ParameterizedType
import java.lang.reflect.TypeVariable

import parser.DataTypes._
import generator.BuildIn
import generator.Helpers

/**
 * Function Table
 */
object Functions {

  /**
   * Prefix of build-in function.
   */
  val prefix = "func_"

  private val WCType1 = WildCardType("T")
  private val WCType2 = WildCardType("U")

  private def ftype(ret: DataType)(args: DataType*) = FunctionType(ret, args.toList)

  /**
   * Build-in function table.
   * It is made from generator.BuildIn class.
   * 
   * @see generator.BuildIn
   */
  private val buildInFunctionTable = classOf[BuildIn].getMethods().filter { method =>
    val mod = method.getModifiers()
    Modifier.isPublic(mod) && Modifier.isStatic(mod) && method.getName.startsWith(prefix)
  } .map { method =>
    val name = method.getName.substring(prefix.length)
    val retType   = innterType(method.getGenericReturnType)
    val paramType = method.getGenericParameterTypes.map(innterType)

    assert(name.length > 0)

    (name, ftype(retType)(paramType:_*))
  }

  private def innterType(t: Type): DataType = {
    val LongClass        = classOf[java.lang.Long]
    val DoubleClass      = classOf[java.lang.Double]
    val TimeClass        = classOf[Helpers.Time]
    val FingerPrintClass = classOf[Helpers.FingerPrint]
    val StringClass      = classOf[String]
    val BytesClass       = classOf[Helpers.ByteStringWrapper]
    val ObjectClass      = classOf[Object]
    val ListClass        = classOf[java.util.List[_]]
    val MapClass         = classOf[java.util.Map[_, _]]

    def noMapping = error("No mapping for type %s".format(t.toString))

    t match {
      case LongClass        => IntType
      case DoubleClass      => FloatType
      case TimeClass        => TimeType
      case FingerPrintClass => FingerPrintType
      case StringClass      => StringType
      case BytesClass       => BytesType
      case t: ParameterizedType =>
        t.getRawType match {
          case ListClass => 
            val Array(con) = t.getActualTypeArguments()

            ArrayType(innterType(con))

          case MapClass  => 
            val Array(key, value) = t.getActualTypeArguments()
            
            MapType(innterType(key), innterType(value))

          case _ => noMapping
        }
      case t: TypeVariable[_] => WildCardType(t.getName)
      case _                  => noMapping
    }
  }

  /**
   * Build-in functions table.
   */
  lazy val funcTableForAnalysis = buildInFunctionTable.foldLeft(Map[String, DataType]()) { case (con, (name, funcType)) =>
    val FunctionList(l) = con.getOrElse(name, FunctionList(Nil))
    con.updated(name, FunctionList(funcType::l))
  }
}
